package cn.sunxiansheng.intelligent.analysis.utils;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MethodCallChainUtil {

    /**
     * 从堆栈信息中提取每个方法的调用链
     *
     * @param stackTrace  堆栈信息
     * @param giteeRepo   Gitee 仓库地址
     * @param moduleName  模块名
     * @param branchName  分支名称
     * @param classPrefix 类路径前缀过滤器
     * @return 每个方法的调用链列表
     * @throws Exception 异常信息
     */
    public static List<String> extractMethodCallChainsFromStackTrace(
            String stackTrace,
            String giteeRepo,
            String moduleName,
            String branchName,
            String classPrefix) throws Exception {
        List<String> callChains = new ArrayList<>();

        // 正则匹配堆栈中的类路径和方法名
        Pattern pattern = Pattern.compile("at ([\\w\\.]+)\\.([\\w]+)\\((\\w+\\.java):(\\d+)\\)");
        Matcher matcher = pattern.matcher(stackTrace);

        while (matcher.find()) {
            String classPath = matcher.group(1); // 类路径
            String methodName = matcher.group(2); // 方法名

            // 过滤掉不符合指定前缀的类
            if (!classPath.startsWith(classPrefix)) {
                continue;
            }

            // 从 Gitee 仓库获取类文件内容
            try {
                String classContent = readClassFileFromGitee(classPath, giteeRepo, moduleName, branchName);
                // 获取方法的调用链
                String methodCallChain = extractMethodCallChain(classContent, methodName);
                callChains.add("类: " + classPath + "\n" + methodCallChain);
            } catch (Exception e) {
                System.err.println("无法解析方法 " + methodName + " 于类: " + classPath);
            }
        }

        return callChains;
    }

    /**
     * 从 Gitee 仓库中读取类文件内容
     *
     * @param classPath  类路径
     * @param giteeRepo  Gitee 仓库地址
     * @param moduleName 模块名
     * @param branchName 分支名称
     * @return 类文件内容字符串
     * @throws Exception 如果类文件不存在或读取失败
     */
    private static String readClassFileFromGitee(
            String classPath,
            String giteeRepo,
            String moduleName,
            String branchName) throws Exception {
        String filePath = "src/main/java/" + classPath.replace(".", "/") + ".java";
        String url = String.format("%s/raw/%s/%s/%s", giteeRepo, branchName, moduleName, filePath);

        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setRequestMethod("GET");

        if (connection.getResponseCode() != 200) {
            throw new IllegalArgumentException("无法从 Gitee 获取类文件: " + url);
        }

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
            return reader.lines().reduce((a, b) -> a + "\n" + b).orElse("");
        }
    }

    /**
     * 提取方法的调用链
     *
     * @param classContent 类文件内容
     * @param methodName   方法名
     * @return 方法调用链
     * @throws Exception 如果解析失败或方法未找到
     */
    private static String extractMethodCallChain(String classContent, String methodName) throws Exception {
        JavaParser javaParser = new JavaParser();
        ParseResult<CompilationUnit> parseResult = javaParser.parse(classContent);

        if (!parseResult.isSuccessful() || !parseResult.getResult().isPresent()) {
            throw new IllegalArgumentException("无法解析类文件内容");
        }

        CompilationUnit compilationUnit = parseResult.getResult().get();
        Optional<ClassOrInterfaceDeclaration> classDeclarationOpt = compilationUnit.findFirst(ClassOrInterfaceDeclaration.class);
        if (!classDeclarationOpt.isPresent()) {
            throw new IllegalArgumentException("未找到类定义");
        }

        ClassOrInterfaceDeclaration classDeclaration = classDeclarationOpt.get();

        // 使用队列递归查找调用链
        Queue<String> methodQueue = new LinkedList<>();
        Set<String> processedMethods = new HashSet<>();
        methodQueue.add(methodName);

        StringBuilder callChain = new StringBuilder();
        callChain.append("调用链:\n");

        // 开始递归提取方法内容及调用链
        while (!methodQueue.isEmpty()) {
            String currentMethodName = methodQueue.poll();
            if (processedMethods.contains(currentMethodName)) {
                continue; // 防止重复处理方法
            }
            processedMethods.add(currentMethodName);

            Optional<MethodDeclaration> methodOpt = classDeclaration.findAll(MethodDeclaration.class).stream()
                    .filter(method -> method.getNameAsString().equals(currentMethodName))
                    .findFirst();

            if (!methodOpt.isPresent()) {
                callChain.append("未找到方法: ").append(currentMethodName).append("\n");
                continue;
            }

            MethodDeclaration method = methodOpt.get();
            callChain.append("方法: ").append(currentMethodName).append("\n")
                    .append(method).append("\n\n");

            // 查找调用此方法的其他方法
            for (MethodDeclaration callerMethod : classDeclaration.findAll(MethodDeclaration.class)) {
                if (!processedMethods.contains(callerMethod.getNameAsString())) {
                    boolean callsTarget = callerMethod.findAll(MethodCallExpr.class).stream()
                            .anyMatch(call -> call.getNameAsString().equals(currentMethodName));
                    if (callsTarget) {
                        methodQueue.add(callerMethod.getNameAsString());
                        callChain.append("方法 '").append(callerMethod.getNameAsString())
                                .append("' 调用了方法 '").append(currentMethodName).append("':\n");
                        callChain.append(callerMethod).append("\n\n");
                    }
                }
            }
        }

        return callChain.toString();
    }
}
